초기화 컨테이너
개요
init container.
직관적으로만 봐도 초기화를 진행하는 컨테이너라는 것을 알 수 있다.
이걸 통해 앱 컨테이너가 동작하기 전에 기본적으로 구축해야 할 설정과 동작을 수행할 수 있다.
참고로 여기에서 파생되어 나오는 것 중 하나가 사이드카 컨테이너이다.
특징
개념 자체는 쉬우니 바로 특징으로 넘어가도록 하자.
- 초기화 컨테이너는 반드시 종료되어야만 한다.
- 앱 컨테이너가 프로세스의 지속적인 작동을 전제하는 것과 다르다!
- 여러 개의 초기화 컨테이너를 둘 수 있다.
- 각 컨테이너는 순서대로 동작하며, 반드시 이전 컨테이너가 성공적으로 종료되었을 때 다음 컨테이너가 실행된다.
- 초기화 컨테이너가 성공적으로 종료되어야 비로소 앱 컨테이너가 동작한다.
- 초기화 컨테이너가 실패하면 kubelet은 파드 속 컨테이너의 장애#재시작 정책(restartPolicy)에 따라 관리한다.
위와 같이 초기화 컨테이너가 실행 중일 때 파드는 Pending
상태에 머물러 있다.
다른 컨테이너와의 차이
- 리소스 제한 방식이 조금 다르다
- 앱 컨테이너와 리소스를 공유하지만, 동시에 돌아가는 일은 없다.
- 다만 볼륨에 선작업을 하는 듯이 작용하는 것은 가능하다.
- 라이프사이클, 컨테이너 프로브는 지원되지 않는다.
- 파드의 running 상태를 위해 종료되어야 하는 컨테이너이기 때문
- T-초기화 컨테이너에 프로브 넣기
- 사이드카 컨테이너는 다르니까 거기에서 참고하라.
사용법
초기화 컨테이너는 다음의 세 가지 상황에서 매우 유용하게 사용할 수 있다.
- 앱 컨테이너가 띄워지기 이전 확실하게 선행해야 하는 조건이 있을 때
- 다른 서비스가 켜져야만 제대로 돌아가는 파드라고 쳐보자.
- 이걸 컨테이너 프로브#startupProbe로 해결하는 방법도 있겠지만, 이는 지속적인 통신을 유발하고, 불필요하게 컨테이너가 오래 떠있게 만든다.
- 초기화 컨테이너에서 처리해버리면 불필요하게 앱 컨테이너가 오래 지속될 필요도 없다.
- 앱 이미지에서 하기 싫은 작업을 선행하고 싶을 때
- 앱 이미지 자체에 없는 디버깅 툴이나 설정 프로그램을 여기에서 선제적으로 깔고 작업을 진행할 수 있다.
- 필요하다면 볼륨으로 나중에 넘기면 된다.
- 앱 컨테이너에서 보안 취약점이 될 여지가 있는 유틸리티를 두지 않아도 된다.
- 앱 이미지에서 할 수 없는 작업을 할 때
- 초기화 컨테이너는 일반 앱 컨테이너와 다르게 Secret의 값을 뜯어볼 수 있다.
용례
for i in {1..100}; do sleep 1; if nslookup myservice; then exit 0; fi; done; exit 1
다른 서비스가 확실히 만들어지기 전까지 기다리기.
이 예시는 앱 컨테이너에 nslookup
이 없어도 돼서 더 좋다.
curl -X POST http://$MANAGEMENT_SERVICE_HOST:$MANAGEMENT_SERVICE_PORT/register -d 'instance=$(<POD_NAME>)&ip=$(<POD_IP>)'
Downward API를 사용해 파드의 정보를 앱 실행 전에 등록하고 싶을 때.
sleep 60
그냥 시간 기다리기
이밖에도 깃 주소를 받아서 앱 컨테이너에 볼륨으로 공유하기, 템플릿 툴을 사용해 설정 파일을 앱 컨테이너에 공유하기 등의 작업을 수행할 수 있다.
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
labels:
app.kubernetes.io/name: MyApp
spec:
containers:
- name: myapp-container
image: busybox:1.28
command: ['sh', '-c', 'echo The app is running! && sleep 3600']
initContainers:
- name: init-myservice
image: busybox:1.28
command: ['sh', '-c', "until nslookup myservice.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for myservice; sleep 2; done"]
- name: init-mydb
image: busybox:1.28
command: ['sh', '-c', "until nslookup mydb.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for mydb; sleep 2; done"]
위와 같이 until을 사용해서 내가 원하는 서비스가 올라올 때까지 대기를 시키는 방식은 매우 유용하다.
유의 사항
몇 가지 유의할 만한 사항이 있다.
초기화 컨테이너를 사용하는데 있어서 알아두면 둬야 할 상세 동작들이라고 할 수 있겠다.
재시작
restartPolicy에 대해
파드의 파드 속 컨테이너의 장애#재시작 정책(restartPolicy)을 따르지만, Always
인 경우에는 OnFailure
가 적용된다.
달리 말하자면 기본적으로 OnFailure
이지만 파드의 재시작 정책이 Never
인 경우에는 Never
로 적용된다고 표현할 수도 있을 것 같다.
초기화 컨테이너가 성공적으로 수행되도 재시작된다면, 앱 컨테이너가 영원히 동작할 수 없으니 어쩌면 당연하다.
다른 문서에서 애매하게 적혀 있어서 T-초기화 컨테이너는 재시작되는가하는 고민까지 하게 만들어준 공식 문서에 다시금 감사를 표한다..
앱 컨테이너 정책 | 초기화 컨테이너 정책 |
---|---|
Always | OnFailure |
OnFailure | OnFailure |
Never | Never |
이렇게 정리해볼 수 있을 듯하다. |
파드의 재시작
초기화 컨테이너는 파드가 동작하는 시점에서 가장 선행되는 프로세스이다.
파드가 동작하는 시점이란 파드를 위한 네트워크, 스토리지 세팅이 완료된 이후를 말한다.
그래서 파드=초기화 컨테이너 정도의 밀접한 관계를 맺는다고도 말할 수 있다.
달리 말해 파드가 재시작하면 모든 초기화 컨테이너도 다시 실행한다.
이건 당연하게 들린다.
그러나 1.20 버전 이전에는 그 역도 거의 성립하다시피 했다.
현재는 그렇지는 않다고 하는 듯하다.
다음은 파드가 재시작되는 경우.
- 파드의 인프라 환경이 재설정됨.
- 실질적으로 누군가 루트 권한으로 노드를 조작한 것과 동일하기에 특수한 케이스
- 초기화 컨테이너 이미지를 변경함
- 재시작 정책이
Always
인데 모든 초기화 컨테이너의 실행 정보가 가비지 컬렉팅됐고, 또한 모든 컨테이너가 종료되었음- 어차피 재시작을 걸긴 해야 하는데, 마침 초기화 컨테이너도 전부 실행 정보다 날아 버리면 그냥 파드가 재시작되는 ㄷㄷ
위 상황들이 궁금해서 조금 T-초기화 컨테이너의 이미지 바꾸기 테스트를 해봤다.
문서와는 조금 다른 구석이 있는 것 같으니 문서 설명을 너무 맹신하지는 말자.
결국 1.20 버전 이후부터는 첫번째 경우 말고는 파드가 재시작되는 경우가 없는 것으로 생각하면 될 듯하다.
리소스 공유
각 파드는 적정한 리소스 값을 가진다.
그리고 그것은 당연히 내부 컨테이너가 가질 수 있는 리소스 값에 달려있다.
이걸 왜 초기화 컨테이너에서 언급하는가?
초기화 컨테이너 중에서 가장 높게 리소스 제한이 걸린 값을 유효 초기 요청/제한
이라고 부른다.
초기화 컨테이너 중 리소스 명시가 안 된 컨테이너는 이 값을 기준으로 제한이 걸린다.
이게 중요한 이유는 파드가 받을 수 있는 리소스의 값에 영향을 준다는 것이다.
파드의 리소스 스케줄링은 다음 중 더 높은 값을 토대로 이뤄진다.
- 모든 앱 컨테이너의 리소스 요청/제한 총량
- 유효한 초기 요청/제한
무슨 말이냐, 초기화 컨테이너는 파드의 라이프사이클에는 영향을 주지도 않는 주제에 파드의 리소스를 많이 걸어버릴 수도 있다는 것이다.
참고로 초기 컨테이너와 앱 컨테이너의 QoS 계층은 동일하다.
팁
케이스가 많지는 않지만, 초기화 컨테이너 역시 재시작될 가능성은 분명히 존재한다.
가령 모종의 이유로 컨테이너가 실패하게 된다던가 하는 상황이다.
그러니 멱등(idempotent)한 코드를 짜는 것이 중요하다.
EmptyDirs
에 이전 초기화 컨테이너가 실행한 결과로 인한 오작동에 대한 대비책은 마련되어 있어야만 한다.
activeDeadlineSeconds
를 써서 초기화 컨테이너가 무한 지속되는 상황을 미연에 방지할 수 있다.
그러나 이건 앱 컨테이너에도 적용되니까 한번 실행되고 끝나는 Job에 대해서만 적용하길 추천한다.
초기화 컨테이너끼리의 이름은 고유해야만 한다.